
#include <stdio.h>     // Standard input and output
#include <unistd.h>    // POSIX standard interfaces
#include "ohos_init.h" // For initializing services and features
#include "cmsis_os2.h" // CMSIS-RTOS API V2
#include "../inc/sle_serve_all.h" // Functional channel
#include "../inc/sle_server.h" // SLE server interface
#include "iot_gpio.h" // OpenHarmony HAL: IoT hardware device operation interface - GPIO
#include "adc.h"
#include "pinctrl.h"
#include "../inc/sle_serve_all.h"
#include <stdlib.h>
#include <memory.h>
#include "common_def.h"
#include "soc_osal.h"
#include "test_suite_log.h"
#include "systick.h"
#include "iot_pwm.h"
#include "iot_errno.h"
#include "iot_watchdog.h"
#include "../inc/iot_gpio_ex.h"
#include <stdio.h>  // Standard input and output
#include <unistd.h> // POSIX standard interfaces
#include "adc/adc_porting.h"

/**
 * @brief ADC callback function to process the data received from a specific ADC channel.
 *
 * This function is triggered when an ADC conversion for a specific channel is complete. 
 * It calculates the average of the received samples, excluding the first one which is considered invalid,
 * then computes the TDS (Total Dissolved Solids) value based on the average voltage.
 * If the TDS value is out of the normal range (assumed to be between 200 and 600),
 * it prints a warning message.
 *
 * @param ch Channel number of the ADC that has completed conversion.
 * @param buffer Pointer to the array containing the converted values.
 * @param length Number of elements in the buffer.
 * @param next Pointer to a boolean flag indicating if the next conversion should occur.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static void adc_callback(uint8_t ch, uint32_t *buffer, uint32_t length, bool *next)
{
    // If ch is SENSOR_CHAN_NAME, calculate the average
    if (ch == Q_SENSOR_CHAN_NAME)
    {
        uint32_t sum = 0;

        // Skip the first invalid value
        if (length <= 1)
        {
            osal_printk("[IRQ]channel: %d, no valid data\r\n", ch);
            return;
        }

        // Start from 1 to skip the first invalid value
        for (uint32_t i = 1; i < length; i++)
        {
            sum += buffer[i];
        }
        uint32_t avg = sum / (length - 1);
        uint32_t tds = avg*avg*avg*66.71-127.93*avg*avg+428.7*avg-2068665;

        osal_printk("ADC channel: %d, avg voltage: %dmv\r\n", ch, avg);
        osal_printk("TDS: %d\r\n", tds);
        // Assuming the normal water quality reference range is between 200 to 600
        if (tds < 0 || tds > 1000) {
            osal_printk("Warning: Water quality is out of normal range!\r\n");
        } else {
            
            osal_printk("Water quality is within normal range.\r\n");
        }
    }
}

/**
 * @brief Initializes and manages ADC sampling for a specific sensor channel.
 *
 * This function sets up the ADC with the specified clock configuration and power settings,
 * configures the ADC scan parameters, and enters an infinite loop where it enables automatic
 * scanning of the designated ADC channel at regular intervals. After each scan, it disables 
 * the auto-scan mode and delays for a short period before repeating the process.
 *
 * The ADC requires 16 clock cycles per sample, so the sampling rate is calculated as adc_clk / 16.
 * It powers on and enables the ADC in normal precision mode.
 *
 * The function uses an ADC scan configuration structure to set up the type of scan (FIFO full or threshold),
 * the lower and upper voltage thresholds for threshold scanning, and the frequency for scanning all channels.
 *
 * In the work cycle, it calls SlideTask() before enabling the auto-scan for the designated channel,
 * waits for 10 milliseconds, then disables the auto-scan and waits for another 30 milliseconds before repeating.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static void ADCTask(void)
{
    // ADC requires 16 clock cycles per sample, thus sampling rate = adc_clk / 16
    uapi_adc_init(ADC_CLOCK_NONE); // Initialize ADC without setting a specific clock

    // Power on and enable ADC in normal precision mode
    uapi_adc_power_en(AFE_GADC_MODE, true); 

    adc_scan_config_t config = {
        .type = 0,          // FIFO full scan or threshold scan
        .threshold_l = 0.0, // Lower threshold voltage (V) for threshold scanning
        .threshold_h = 2.3, // Upper threshold voltage (V) for threshold scanning
        .freq = 0,          // ADC scan frequency for all channels
    };

    // Work loop, get the value of the ADC channel every 100ms
    while (1)
    {
        SlideTask();
        uapi_adc_auto_scan_ch_enable(Q_SENSOR_CHAN_NAME, config, adc_callback);
        osDelay(10); // Wait for 10 milliseconds
        uapi_adc_auto_scan_ch_disable(Q_SENSOR_CHAN_NAME);

        osDelay(30); // Wait for another 30 milliseconds
    }
}


/**
 * @brief Outputs a pulse signal with a specified duty cycle.
 *
 * This function generates a pulse signal where the high level lasts for `duty` microseconds,
 * and the low level lasts for `20000 - duty` microseconds, resulting in a total period of 20000 microseconds.
 * It is typically used to control the angle of a servo motor by setting the duration of the high pulse.
 *
 * The function configures the GPIO pin connected to the servo motor as an output,
 * sets it to high for `duty` microseconds, then sets it to low for the remainder of the 20000 microsecond period.
 *
 * @param duty The duration in microseconds that the pulse should remain high. 
 *             Must be between 0 and 20000 inclusive.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void SetAngle(unsigned int duty)
{
    unsigned int time = FREQ_TIME; // Total period of the pulse signal (20000 microseconds)
    
    // Set the GPIO pin direction as output
    IoTGpioSetDir(BSP_SG92R, IOT_GPIO_DIR_OUT);
    
    // Set the GPIO pin output level to high
    IoTGpioSetOutputVal(BSP_SG92R, IOT_GPIO_VALUE1);
    
    // Delay for `duty` microseconds (high level)
    uapi_systick_delay_us(duty);
    
    // Set the GPIO pin output level to low
    IoTGpioSetOutputVal(BSP_SG92R, IOT_GPIO_VALUE0);
    
    // Delay for the remaining time in the period (low level)
    uapi_systick_delay_us(time - duty);
}

/**
 * @brief Centers the steering gear by sending a pulse signal with a high level of 1500 microseconds.
 *
 * This function ensures that the steering gear (servo motor) is centered by continuously sending
 * a pulse signal where the high level lasts for 1500 microseconds, and the low level lasts for 
 * the remainder of the 20000 microsecond period. The 1500 microseconds high pulse corresponds to 
 * the center position for most servo motors.
 *
 * It calls `SetAngle` with the angle parameter set to 1500 microseconds to generate the required pulse,
 * and repeats this action a predefined number of times (`COUNT`), ensuring the servo has enough time 
 * to reach and stabilize at the center position.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void RegressMiddle(void)  // Center position (equivalent to 0° for some servos)
{
    unsigned int angle = 1500;                // High level duration of 1500 microseconds

    // Send the centering pulse signal multiple times to ensure the servo reaches the center position
    for (int i = 0; i < COUNT; i++) {
        SetAngle(angle);
    }
}

/**
 * @brief Turns the steering gear 90 degrees to the right.
 *
 * This function controls the steering gear (servo motor) to rotate 90 degrees to the right by sending
 * a pulse signal where the high level lasts for 500 microseconds. The 500 microseconds high pulse 
 * corresponds to the -90° position for most servo motors, which is typically the fully rightward position.
 *
 * It calls `SetAngle` with the angle parameter set to 500 microseconds to generate the required pulse,
 * and repeats this action a predefined number of times (`COUNT`), ensuring the servo has enough time 
 * to reach and stabilize at the -90° position.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void EngineTurnRight(void)  // Turn to -90° (fully right)
{
    unsigned int angle = 500;                   // High level duration of 500 microseconds

    // Send the turning pulse signal multiple times to ensure the servo reaches the -90° position
    for (int i = 0; i < COUNT; i++) {
        SetAngle(angle);
    }
}

/**
 * @brief Turns the steering gear 45 degrees to the right and opens the water valve.
 *
 * This function controls the steering gear (servo motor) to rotate 45 degrees to the right by sending
 * a pulse signal where the high level lasts for 1000 microseconds. The 1000 microseconds high pulse 
 * corresponds to the -45° position for most servo motors, which is typically halfway between the center 
 * and fully rightward positions.
 *
 * It calls `SetAngle` with the angle parameter set to 1000 microseconds to generate the required pulse,
 * and repeats this action a predefined number of times (`COUNT`), ensuring the servo has enough time 
 * to reach and stabilize at the -45° position.
 *
 * Additionally, this function is intended to open the water valve, presumably by positioning the servo
 * in such a way that it allows water flow.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void EngineTurnRight_half(void)  // Turn to -45° (half right) and open valve
{
    unsigned int angle = 1000;   // High level duration of 1000 microseconds

    // Send the turning pulse signal multiple times to ensure the servo reaches the -45° position
    for (int i = 0; i < COUNT; i++) {
        SetAngle(angle);
    }

    // Optionally add delay here if needed for the servo to settle before opening the valve
    osDelay(50); // Adjust delay as necessary

    // Open the water valve after positioning the servo
    OpenValve(); // Assuming there's a function to open the valve
}

/**
 * @brief Turns the steering gear 45 degrees to the left.
 *
 * This function controls the steering gear (servo motor) to rotate 45 degrees to the left by sending
 * a pulse signal where the high level lasts for 2000 microseconds. The 2000 microseconds high pulse 
 * corresponds to the +45° position for most servo motors, which is typically halfway between the center 
 * and fully leftward positions.
 *
 * It calls `SetAngle` with the angle parameter set to 2000 microseconds to generate the required pulse,
 * and repeats this action a predefined number of times (`COUNT`), ensuring the servo has enough time 
 * to reach and stabilize at the +45° position.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void EngineTurnLeft_half(void)  // Turn to +45° (half left)
{
    unsigned int angle = 2000;   // High level duration of 2000 microseconds

    // Send the turning pulse signal multiple times to ensure the servo reaches the +45° position
    for (int i = 0; i < COUNT; i++) {
        SetAngle(angle);
    }

    // Optionally add delay here if needed for the servo to settle before continuing
    osDelay(50); // Adjust delay as necessary
}

/**
 * @brief Turns the steering gear 90 degrees to the left.
 *
 * This function controls the steering gear (servo motor) to rotate 90 degrees to the left by sending
 * a pulse signal where the high level lasts for 2500 microseconds. The 2500 microseconds high pulse 
 * corresponds to the +90° position for most servo motors, which is typically the fully leftward position.
 *
 * It calls `SetAngle` with the angle parameter set to 2500 microseconds to generate the required pulse,
 * and repeats this action a predefined number of times (`COUNT`), ensuring the servo has enough time 
 * to reach and stabilize at the +90° position.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void EngineTurnLeft(void)  // Turn to +90° (fully left)
{
    unsigned int angle = 2500;   // High level duration of 2500 microseconds

    // Send the turning pulse signal multiple times to ensure the servo reaches the +90° position
    for (int i = 0; i < COUNT; i++) {
        SetAngle(angle);
    }

    // Optionally add delay here if needed for the servo to settle before continuing
    osDelay(50); // Adjust delay as necessary
}


/**
 * @brief Executes a sequence of commands to test the SG90 servo motor.
 *
 * This function performs a series of operations on the SG90 servo motor:
 * 1. Prints a start message.
 * 2. Turns the steering gear 45 degrees to the left by calling `EngineTurnLeft_half`.
 * 3. Delays for 200 milliseconds to allow the servo to reach its position.
 * 4. Prints a confirmation message for turning 45 degrees to the left.
 * 5. Centers the steering gear by calling `RegressMiddle`.
 * 6. Delays for 200 milliseconds to allow the servo to reach its center position.
 * 7. Prints a confirmation message for centering the steering gear.
 * 8. Adds an additional delay and prints a final message confirming that the steering gear is centered.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
void Sg90Task(void)
{
    osal_printk("Start testing SG90\r\n");

    // Turn the steering gear 45 degrees to the left
    EngineTurnLeft_half();
    osDelay(200); // Delay for 200 milliseconds
    osal_printk("Turned 45 degrees to the left of the steering gear\r\n");

    // Center the steering gear
    RegressMiddle();
    osDelay(200); // Delay for 200 milliseconds
    osal_printk("Steering gear is now centered\r\n");

    osDelay(200); // Additional delay before finishing
    osal_printk("The steering gear is centered.\r\n");
}

/**
 * @brief Main thread function that initializes services and runs a continuous loop of tasks.
 *
 * This function serves as the main execution thread for the application:
 * 1. Initializes the SLE server.
 * 2. Resets the slide mechanism with `Slide_Reset`.
 * 3. Enters an infinite loop where it sequentially calls `Sg90Task`, `SlideTask`, and `ADCTask` to perform their respective operations.
 *
 * The `Sg90Task` handles testing the SG90 servo motor by turning it left, centering it, and printing status messages.
 * The `SlideTask` manages the slide mechanism's operations.
 * The `ADCTask` manages ADC sampling for a specific sensor channel.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static void SleTask(void)
{
    // Initialize the SLE server
    sle_server_init();

    // Reset the slide mechanism
    Slide_Reset();

    // Continuous task loop
    while (1)
    {
        // Perform SG90 servo motor test sequence
        Sg90Task();

        // Manage slide mechanism operations
        SlideTask();

        // Manage ADC sampling for a specific sensor channel
        ADCTask();
    }
}

/**
 * @brief Entry function for the SLE server demo.
 *
 * This function initializes and starts a new thread named "SleTask" which serves as the main execution thread.
 * It defines the thread attributes, including name, stack size, and priority, and then attempts to create the thread.
 * If successful, it prints a success message; otherwise, it prints an error message.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static void SleServerDemo(void)
{
    // Define thread attributes
    osThreadAttr_t attr = {
        .name = "SleTask",
        .stack_size = 1024 * 10, // Stack size in bytes, suitable for CHIP_WS63
        .priority = osPriorityNormal,
    };

    // Create the thread
    if (osThreadNew(SleTask, NULL, &attr) == NULL) {
        osal_printk("[SleServerDemo] Failed to create SleTask!\n");
    } else {
        osal_printk("[SleServerDemo] Created SleTask successfully!\n");
    }
}

// Run the entry function at system startup
SYS_RUN(SleServerDemo);
